package crmdna.client.counter;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;

import com.googlecode.objectify.VoidWork;

import static crmdna.common.OfyService.ofy;
import crmdna.common.APIException;
import crmdna.common.Utils;
import crmdna.common.APIResponse.Status;

class CounterCore {		
	Random random = new Random();
	int numShards; //once set for a counter cannot be changed
			
	protected CounterCore(int numShards) {
		if (numShards < 0)
			Utils.throwIncorrectSpecException("numShards cannot be negative");
		
		final int MAX_SHARDS = 100;
		if (numShards > MAX_SHARDS)
			Utils.throwIncorrectSpecException("Only [" + MAX_SHARDS + "] allowed");
		
		this.numShards = numShards;
	}
	
	private String getShardKey(String counterName, int shardIndex) {
		return counterName + "_shard" + shardIndex;
	}
	
	public void increment(final String namespace, String counterName, final int n) {
		int shardIndex = random.nextInt(numShards);		
		
		final String key = getShardKey(counterName, shardIndex);
		
		final int MAX_KEY_LENGTH = 100;
		if (key.length() > MAX_KEY_LENGTH)
			throw new APIException(Status.ERROR_RESOURCE_INCORRECTLY_SPECIFIED, 
					"Counter key [" + key + "] is more than [" + MAX_KEY_LENGTH + "] chars");
		
		ofy(namespace).transact(new VoidWork() {
			
			@Override
			public void vrun() {
				ShardEntity se = ofy(namespace).load().type(ShardEntity.class).id(key).get();
				if (se == null) {
					se = new ShardEntity();
					se.key = key;
					se.count = n;
				} else {
					se.count += n;
				}
				
				ofy(namespace).save().entity(se);				
			}
		});			
	}
	
	public long getCount(String namespace, String counterName) {
		List<String> keys = new ArrayList<>();			
		for (int shardIndex = 0; shardIndex < numShards; shardIndex++) {
			keys.add(getShardKey(counterName, shardIndex));
		}
		
		Map<String, ShardEntity> map = ofy(namespace).load().type(ShardEntity.class)
				.ids(keys);
		
		long count = 0;
		for (String key : map.keySet()) {
			ShardEntity se = map.get(key);
			if (se != null)
				count += se.count;
		}
		
		return count;
	}
}
